/*
 * @(#)Metadata.java   1.0   May 7, 2007
 *
 * Copyright 2000-2007 ETH Zurich. All Rights Reserved.
 *
 * This software is the proprietary information of ETH Zurich.
 * Use is subject to license terms.
 *
 * @(#) $Id: Metadata.java 435 2007-05-23 12:54:49Z D\aldespin $
 */
package ch.ethz.globis.javaom.system;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import ch.ethz.globis.javaom.core.AttributeValue;
import ch.ethz.globis.javaom.core.Bulk;
import ch.ethz.globis.javaom.core.Object;
import ch.ethz.globis.javaom.core.Tuple;

/**
 * @author aldespin
 * @version 1.0
 */
public class Metadata {

   public static final String OBJECT_HAS_TYPES = "objectHasTypes";
   public static final String TYPE_HAS_ATTRIBUTES = "typeHasAttributes";
   public static final String OBJECT_HAS_ATTRIBUTEVALUES = "objectHasAttributeValues";
   public static final String ATTRIBUTEVALUES = "attributeValues";
   public static final String TYPE_HAS_EXTENT = "typeHasExtent";
   public static final String TYPE_ISA = "typeISA";
   public static final String COLLECTION_ISA = "collectionISA";

   public static final String OBJECT = "object";
   public static final String TYPE = "type";
   public static final String ATTRIBUTE = "attribute";
   public static final String COLLECTION = "collection";
   public static final String BINARYCOLLECTION = "binaryCollection";

   public static final String BULK = "bulk";
   public static final String TUPLE = "tuple";
   public static final String STRING = "string";

   public static final String NAME = "name";
   public static final String MEMBERTYPE = "memberType";
   public static final String EXTENT = "extent";
   public static final String DOMAIN_MEMBERTYPE = "domainMemberType";
   public static final String RANGE_MEMBERTYPE = "rangeMemberType";
   public static final String DOMAIN_VALUE = "domainValue";
   public static final String RANGE_VALUE = "rangeValue";

   protected static final String TYPENAME = "_typeName";
   private static final String EXTENTCOLLECTION = "s";
   private static final String VALUEOBJECT = "_value";
   private static final String BULKVALUE = "_bulk";

   static {
      Metadata.METADATA = Bootstrap.bootstrap();
      Logger.getLogger(Metadata.class.getName()).log(Level.FINE, Metadata.METADATA.toString());
   }

   public static Metadata METADATA;

   private Map<String, Object> map;
   private Map<Object, String> inverse;
   private java.util.Collection<Object> attributes;
   private java.util.Collection<Object> binaryCollections;
   private java.util.Collection<Object> extentCollections;
   private java.util.Collection<Object> strings;
   private java.util.Collection<Object> bulks;
   private java.util.Collection<Object> tuples;
   private java.util.Collection<Object> types;

   public Metadata() {
      this.map = new HashMap<String, Object>();
      this.inverse = new HashMap<Object, String>();
      this.attributes = new ArrayList<Object>();
      this.binaryCollections = new ArrayList<Object>();
      this.extentCollections = new ArrayList<Object>();
      this.strings = new ArrayList<Object>();
      this.bulks = new ArrayList<Object>();
      this.tuples = new ArrayList<Object>();
      this.types = new ArrayList<Object>();
   }

   private void putObject(final String key, final Object object) {
      if (this.map.containsKey(key)) {
         throw new RuntimeException("object '" + key + "' already exists");
      }
      this.map.put(key, object);
      this.inverse.put(object, key);
   }

   private Object getObject(final String key) {
      if (!this.map.containsKey(key)) {
         throw new RuntimeException("object '" + key + "' does not exist");
      }
      return this.map.get(key);
   }

   /**
    * @param object
    * @return null if there is no key for object given.
    */
   public String getObjectKey(final Object object) {
      return this.inverse.get(object);
   }

   protected java.util.Collection<Object> getObjects() {
      return this.map.values();
   }

   protected AttributeValue<String> createStringValue(final String value) {
      final AttributeValue<String> result = new AttributeValue<String>(this, value);
      this.putObject(value + Metadata.VALUEOBJECT + UUID.randomUUID(), result);
      this.strings.add(result);
      return result;
   }

   protected java.util.Collection<Object> getStringValues() {
      return new ArrayList<Object>(this.strings);
   }

   protected Tuple createTuple(final String binaryCollectionName, final Object domain,
         final Object range) {
      final Tuple result = new Tuple(this, domain, range);
      this.putObject(binaryCollectionName + "-" + Metadata.TUPLE + "-" + result.getID(), result);
      this.tuples.add(result);
      return result;
   }

   protected java.util.Collection<Object> getTuples() {
      return new ArrayList<Object>(this.tuples);
   }

   protected void createCollectionExtent(final String collectionName) {
      final Object result = new Bulk(this);
      this.putObject(collectionName + Metadata.BULKVALUE, result);
      this.bulks.add(result);
   }

   protected Bulk getCollectionExtent(final String collectionName) {
      return (Bulk) this.getObject(Metadata.getExtentCollectionName(collectionName)
            + Metadata.BULKVALUE);
   }

   protected java.util.Collection<Object> getExtents() {
      return new ArrayList<Object>(this.bulks);
   }

   protected void createBinaryCollectionExtent(final String binaryCollectionName) {
      final Object result = new Bulk(this);
      this.putObject(binaryCollectionName + Metadata.BULKVALUE, result);
      this.bulks.add(result);
   }

   public Bulk getBinaryCollectionExtent(final String binaryCollectionName) {
      return (Bulk) this.getObject(binaryCollectionName + Metadata.BULKVALUE);
   }

   protected void createTypeObject(final String typeName) {
      final Object result = new Object(this);
      this.putObject(typeName, result);
      this.types.add(result);
   }

   public Object getTypeObject(final String typeName) {
      return this.getObject(typeName);
   }

   protected java.util.Collection<Object> getTypeObjects() {
      return new ArrayList<Object>(this.types);
   }

   protected static String getAttributeName(final String declaringTypeName,
         final String attributeName) {
      return declaringTypeName + attributeName;
   }

   protected void createAttributeObject(final String declaringTypeName, final String attributeName) {
      final Object result = new Object(this);
      this.putObject(declaringTypeName + attributeName, result);
      this.attributes.add(result);
   }

   public Object getAttributeObject(final String declaringTypeName, final String attributeName) {
      return this.getObject(declaringTypeName + attributeName);
   }

   protected java.util.Collection<Object> getAttributeObjects() {
      return new ArrayList<Object>(this.attributes);
   }

   public static String getExtentCollectionName(final String typeName) {

      String first = typeName.substring(0, 1);
      first = first.toUpperCase();
      String rest = typeName.substring(1);

      return first + rest + Metadata.EXTENTCOLLECTION;
   }

   public void createExtentCollection(final String typeName) {
      final Object result = new Object(this);
      this.putObject(Metadata.getExtentCollectionName(typeName), result);
      this.extentCollections.add(result);
   }

   public Object getExtentCollection(final String typeName) {
      return this.getObject(Metadata.getExtentCollectionName(typeName));
   }

   protected java.util.Collection<Object> getExtentCollections() {
      return new ArrayList<Object>(this.extentCollections);
   }

   protected void createBinaryCollection(final String name) {
      final Object result = new Object(this);
      this.putObject(name, result);
      this.binaryCollections.add(result);
   }

   public Object getBinaryCollection(final String name) {
      return this.getObject(name);
   }

   protected java.util.Collection<Object> getBinaryCollections() {
      return new ArrayList<Object>(this.binaryCollections);
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public String toString() {
      final StringBuffer result = new StringBuffer();
      result.append("<metadata>");
      for (Object current : this.bulks) {
         result.append(current.toString());
      }
      result.append("</metadata>");
      return result.toString();
   }
}
